#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/klist.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/kprobes.h>

#include <asm/uaccess.h>

#define HELLO_MAJOR             247
#define HELLO_CHARDEV_NAME      "hello_chardev"
#define HELLO_MAX_BUFFER        256
#define HELLO_CLASS_NAME        "hello_class"

static char *hello_buffer;
static struct class *hello_class;

static int hello_open(struct inode *inode, struct file *file)
{
    printk("[%s %d ]\n", __FUNCTION__, __LINE__);
    return 0;
}


ssize_t hello_read(struct file *file, char __user *user, size_t size, loff_t *offset)
{
    int hello_size = strlen(hello_buffer) + 1;

    if(copy_to_user(user, hello_buffer,hello_size)) {
        return 0;
    }

    *offset = hello_size;
    printk("[%s %d ]\n", __FUNCTION__, __LINE__); 
    return hello_size;
}

ssize_t hello_write(struct file *file, char __user *user, size_t size, loff_t *offset)
{
    if (size < 1) {
        return -EINVAL;
    }

    if(copy_from_user(hello_buffer, user, size)) {
        return -EFAULT;
    }

    *offset += size;
    printk("[%s %d ]\n", __FUNCTION__, __LINE__); 

    return size;
}

static const struct file_operations hello_fops = {
    .owner  = THIS_MODULE,
    .open   = hello_open,
    .read   = hello_read,  
    .write  = hello_write,
};


static int __init hello_init()
{
    hello_class = class_create(THIS_MODULE, HELLO_CLASS_NAME);
    device_create(hello_class, NULL, MKDEV(HELLO_MAJOR, 0), NULL, HELLO_CHARDEV_NAME);
    register_chrdev(HELLO_MAJOR, HELLO_CHARDEV_NAME, &hello_fops);    

    hello_buffer = kzalloc(HELLO_MAX_BUFFER, GFP_KERNEL);
    memset(hello_buffer, 0, HELLO_MAX_BUFFER);

    printk("[%s %d ] success\n", __FUNCTION__, __LINE__); 
    return 0;
}

static void __exit hello_exit()
{
    if (hello_buffer) {
        printk("[%s %d ]\n", __FUNCTION__, __LINE__);
        kfree(hello_buffer);
    }

    device_destroy(hello_class,MKDEV(HELLO_MAJOR, 0));     
    unregister_chrdev(HELLO_MAJOR,HELLO_CHARDEV_NAME);
    class_destroy(hello_class);

    printk("[%s %d ] success\n", __FUNCTION__, __LINE__); 
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("chzhou");
MODULE_DESCRIPTION("hello_chardev");
MODULE_LICENSE("GPL");

